﻿using System;
using System.Globalization;
using System.Windows;
using System.Windows.Media.Animation;

namespace HandyControl.Interactivity;

public abstract class Behavior : Animatable, IAttachedObject
{
    private DependencyObject _associatedObject;
    private readonly Type _associatedType;

    internal Behavior(Type associatedType)
    {
        _associatedType = associatedType;
    }

    protected DependencyObject AssociatedObject
    {
        get
        {
            ReadPreamble();
            return _associatedObject;
        }
    }

    protected Type AssociatedType
    {
        get
        {
            ReadPreamble();
            return _associatedType;
        }
    }

    public void Attach(DependencyObject dependencyObject)
    {
        if (!Equals(dependencyObject, AssociatedObject))
        {
            if (AssociatedObject != null)
                throw new InvalidOperationException(ExceptionStringTable
                    .CannotHostBehaviorMultipleTimesExceptionMessage);
            if (dependencyObject != null && !AssociatedType.IsInstanceOfType(dependencyObject))
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
                    ExceptionStringTable.TypeConstraintViolatedExceptionMessage,
                    new object[] { GetType().Name, dependencyObject.GetType().Name, AssociatedType.Name }));
            WritePreamble();
            _associatedObject = dependencyObject;
            WritePostscript();
            OnAssociatedObjectChanged();
            OnAttached();
        }
    }

    public void Detach()
    {
        OnDetaching();
        WritePreamble();
        _associatedObject = null;
        WritePostscript();
        OnAssociatedObjectChanged();
    }

    DependencyObject IAttachedObject.AssociatedObject =>
        AssociatedObject;

    internal event EventHandler AssociatedObjectChanged;

    protected override Freezable CreateInstanceCore()
    {
        return (Freezable) Activator.CreateInstance(GetType());
    }

    private void OnAssociatedObjectChanged()
    {
        AssociatedObjectChanged?.Invoke(this, new EventArgs());
    }

    protected virtual void OnAttached()
    {
    }

    protected virtual void OnDetaching()
    {
    }
}
